#!/bin/sh

set -eu

version() { echo 0.8.0; }

usage() {
  printf '%s\n' "Usage: shdotenv [OPTION]... [--] [COMMAND [ARG]...]" "" \
    "  -d, --dialect DIALECT  Specify the .env dialect [default: posix]" \
    "                           (posix, ruby, node, python, php, go, rust, docker)" \
    "  -s, --shell SHELL      Output in the specified shell format [default: posix]" \
    "                           (posix, fish)" \
    "  -e, --env ENV_PATH     Location of the .env file [default: .env]" \
    "                           Multiple -e options are allowed" \
    "  -o, --overload         Overload predefined environment variables" \
    "  -n, --noexport         Do not export keys without export prefix" \
    "  -g, --grep PATTERN     Output only those that match the regexp pattern" \
    "  -k, --keyonly          Output only variable names" \
    "  -q, --quiet            Suppress all output" \
    "  -v, --version          Show the version and exit" \
    "  -h, --help             Show this message and exit"
}

abort() { echo "shdotenv:" "$@" >&2 && exit 1; }
requires() {
  [ $# -gt 1 ] || abort "Error: $1 option requires an argument"
  _shdotenv_pos=$((_shdotenv_pos + 1))
}

parse_options() {
  while [ $# -gt 0 ]; do
    case $1 in
      -d | --dialect) requires "$@" && shift && _shdotenv_dialect="$1" ;;
      -s | --shell) requires "$@" && shift && _shdotenv_shell="$1" ;;
      -e | --env)
        requires "$@" && shift
        _shdotenv_envfiles="$_shdotenv_envfiles \"\${$_shdotenv_pos}\""
        ;;
      -o | --overload) _shdotenv_overload=1 ;;
      -n | --noexport) _shdotenv_allexport='' ;;
      -g | --grep) requires "$@" && shift && _shdotenv_grep="$1" ;;
      -k | --keyonly) _shdotenv_keyonly=1 ;;
      -q | --quiet) _shdotenv_quiet=1 ;;
      -v | --version) version && exit 0 ;;
      -h | --help) usage && exit 0 ;;
      --) _shdotenv_pos=$((_shdotenv_pos + 1)) && break ;;
      -*) abort "Unknown option: $1" ;;
      *) break ;;
    esac
    shift
    _shdotenv_pos=$((_shdotenv_pos + 1))
  done
}

trim() {
  eval "$1=\${2#\"\${2%%[!\$IFS]*}\"} && $1=\${$1%\"\${$1##*[!\$IFS]}\"}"
}

init_vars() {
  _shdotenv_dialect='' _shdotenv_shell="${SHDOTENV_SHELL:-}" _shdotenv_grep=''
  _shdotenv_envfiles='' _shdotenv_overload='' _shdotenv_allexport=1
  _shdotenv_keyonly='' _shdotenv_quiet=''
  _shdotenv_pos=1 _shdotenv_line=''
  _shdotenv_parser=$(cat src/parser.awk) # @INCLUDE-FILE
}

unset_vars() {
  unset _shdotenv_dialect _shdotenv_shell _shdotenv_grep
  unset _shdotenv_envfiles _shdotenv_overload _shdotenv_allexport
  unset _shdotenv_keyonly _shdotenv_quiet
  unset _shdotenv_pos _shdotenv_line _shdotenv_parser _shdotenv_env
}

read_config_file() {
  [ -e "$1" ] || return 0
  while IFS= read -r _shdotenv_line || [ "$_shdotenv_line" ]; do
    _shdotenv_line=${_shdotenv_line%%#*}
    trim _shdotenv_line "$_shdotenv_line"
    case $_shdotenv_line in
      dialect:*) trim _shdotenv_dialect "${_shdotenv_line#*:}" ;;
      *) abort "Unknown line in $1: $_shdotenv_line" ;;
    esac
  done <"$1"
}

exec_awk() {
  eval "set -- ${_shdotenv_envfiles:-.env}"
  set -- "$_shdotenv_parser" "$@"
  [ "$_shdotenv_dialect" ] && set -- -v DIALECT="$_shdotenv_dialect" "$@"
  [ "$_shdotenv_shell" ] && set -- -v SHELL="$_shdotenv_shell" "$@"
  [ "$_shdotenv_overload" ] && set -- -v OVERLOAD="$_shdotenv_overload" "$@"
  [ "$_shdotenv_allexport" ] && set -- -v ALLEXPORT="$_shdotenv_allexport" "$@"
  [ "$_shdotenv_grep" ] && set -- -v GREP="$_shdotenv_grep" "$@"
  [ "$_shdotenv_keyonly" ] && set -- -v KEYONLY="$_shdotenv_keyonly" "$@"
  "${SHDOTENV_AWK:-awk}" "$@"
}

eval_env() {
  eval "set --; $1"
}

init_vars
parse_options "$@"

read_config_file ".shdotenv"

if [ $(($# - _shdotenv_pos + 1)) -eq 0 ] || [ "$_shdotenv_keyonly" ]; then
  [ "$_shdotenv_quiet" ] && exec >/dev/null
  exec_awk "$@"
else
  _shdotenv_shell="posix"
  _shdotenv_env=$(exec_awk "$@")
  shift $((_shdotenv_pos - 1))
  set -- "$_shdotenv_env" "$@"
  unset_vars
  eval_env "$1"
  shift
  exec "$@"
fi
